package cn.jinbyte.web.utils;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 令牌处理工具类，提供安全的令牌生成、验证和解析功能
 */
public class TokenUtils {
    private static final Logger logger = LoggerFactory.getLogger(TokenUtils.class);

    // 安全随机数生成器
    private static final SecureRandom secureRandom = new SecureRandom();

    // 令牌默认长度（字节）
    private static final int DEFAULT_TOKEN_LENGTH = 32;

    // 哈希算法
    private static final String HASH_ALGORITHM = "SHA-256";

    // 加密算法
    private static final String CRYPTO_ALGORITHM = "HmacSHA256";

    /**
     * 生成随机令牌
     *
     * @return 随机生成的Base64编码令牌
     */
    public static String generateToken() {
        byte[] tokenBytes = new byte[DEFAULT_TOKEN_LENGTH];
        secureRandom.nextBytes(tokenBytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes);
    }

    /**
     * 生成带过期时间的令牌
     *
     * @param expirationSeconds 过期时间（秒）
     * @param secretKey         密钥（用于签名）
     * @return 带过期时间的令牌
     */
    public static String generateExpiringToken(int expirationSeconds, String secretKey) {
        try {
            // 计算过期时间戳
            long expirationTime = Instant.now().getEpochSecond() + expirationSeconds;

            // 生成随机部分
            String randomPart = generateToken();

            // 组合令牌内容
            String content = randomPart + "." + expirationTime;

            // 生成签名
            String signature = generateSignature(content, secretKey);

            // 组合最终令牌
            return content + "." + signature;
        } catch (Exception e) {
            logger.error("生成带过期时间的令牌失败", e);
            throw new RuntimeException("令牌生成失败", e);
        }
    }

    /**
     * 验证带过期时间的令牌
     *
     * @param token     要验证的令牌
     * @param secretKey 密钥（与生成时使用的相同）
     * @return 如果令牌有效且未过期返回true，否则返回false
     */
    public static boolean validateExpiringToken(String token, String secretKey) {
        if (token == null || token.isEmpty() || secretKey == null || secretKey.isEmpty()) {
            return false;
        }

        try {
            // 拆分令牌
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                return false; // 令牌格式不正确
            }

            String randomPart = parts[0];
            String expirationTimeStr = parts[1];
            String signature = parts[2];

            // 验证签名
            String content = randomPart + "." + expirationTimeStr;
            String computedSignature = generateSignature(content, secretKey);
            if (!Objects.equals(signature, computedSignature)) {
                return false; // 签名不匹配
            }

            // 验证过期时间
            long expirationTime;
            try {
                expirationTime = Long.parseLong(expirationTimeStr);
            } catch (NumberFormatException e) {
                return false; // 时间格式不正确
            }

            // 检查是否过期
            return Instant.now().getEpochSecond() < expirationTime;
        } catch (Exception e) {
            logger.error("验证令牌失败", e);
            return false;
        }
    }

    /**
     * 生成签名
     *
     * @param content   要签名的内容
     * @param secretKey 密钥
     * @return 签名
     */
    private static String generateSignature(String content, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
        SecretKey key = new SecretKeySpec(
                secretKey.getBytes(StandardCharsets.UTF_8),
                CRYPTO_ALGORITHM
        );

        javax.crypto.Mac mac = javax.crypto.Mac.getInstance(CRYPTO_ALGORITHM);
        mac.init(key);
        byte[] signatureBytes = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes);
    }

    /**
     * 哈希令牌（用于存储）
     *
     * @param token 原始令牌
     * @return 哈希后的令牌
     */
    public static String hashToken(String token) {
        if (token == null || token.isEmpty()) {
            return null;
        }

        try {
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
            byte[] hashBytes = digest.digest(token.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(hashBytes);
        } catch (NoSuchAlgorithmException e) {
            logger.error("哈希令牌失败", e);
            throw new RuntimeException("令牌哈希失败", e);
        }
    }

    /**
     * 解析令牌中的信息（仅适用于自定义格式的令牌）
     *
     * @param token 令牌
     * @return 包含令牌信息的Map
     */
    public static Map<String, String> parseTokenInfo(String token) {
        Map<String, String> info = new HashMap<>();

        if (token == null || token.isEmpty()) {
            return info;
        }

        try {
            String[] parts = token.split("\\.");
            info.put("partsCount", String.valueOf(parts.length));

            if (parts.length >= 2) {
                info.put("expirationTime", parts[1]);
            }

            return info;
        } catch (Exception e) {
            logger.error("解析令牌信息失败", e);
            return info;
        }
    }
}
